package com.credithc.signature.manager.service.syncsign;

import com.alibaba.fastjson.JSON;
import com.credithc.signature.common.constant.CoreConstant;
import com.credithc.signature.common.po.Template;
import com.credithc.signature.common.po.TemplateExt;
import com.credithc.signature.common.ro.core.CoreRequet;
import com.credithc.signature.common.service.TemplateExtService;
import com.credithc.signature.common.service.TemplateService;
import com.credithc.signature.common.utils.DateUtil;
import com.credithc.signature.common.utils.FreemarkerConfiguration;
import com.credithc.signature.common.utils.PdfGenerator;
import freemarker.template.Configuration;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Semaphore;

/**
 * 描述：同步调用生成pdf服务<br/>
 * 作者：sunyang
 * 创建：2019/7/1 15:25
 * 版本：v1.0.0<br/>
 */
@Slf4j
@Service
public class GeneratePDFServiceSync {

    @Value("${signature.snapNoSignPdf.path:/data/sync_temp/nosign-pdf/}")
    private String snapNoSignPDFPath;

    @Autowired
    private TemplateService templateService;

    @Autowired
    private TemplateExtService templateExtService;

    private ConcurrentHashMap<String, Semaphore> templatesSemaphore = new ConcurrentHashMap<>(16);

    /**
     * 生成未盖章的pdf文件
     *
     * @param coreRequet
     * @return
     */
    public String generatePDF(CoreRequet coreRequet, Map<String, List<Map<String, String>>> listData,
                              Map<String, String> otherData) throws Exception {

        String requestSN = coreRequet.getRequestSn();

        // 1.生成模板参数
        Map<String, Object> templateParams;
        try {
            templateParams = generateTemplateParams(listData, otherData);
        } catch (Exception e) {
            log.error("－消息－:requestSn={},获取模板失败", requestSN);
            throw new RuntimeException(e);
        }

        // 2.获取未签章pdf文件路径
        String noSignSnapPDFPath = getSnapPDFPath(coreRequet.getRequestSn());

        // 3.获取拼装好参数的模板字符串
        String pdfData = getPDFStr(templateParams, coreRequet.getTemplateNo(), coreRequet.getRequestSn());
        generatePDFToDisk(requestSN, noSignSnapPDFPath, pdfData);

        return noSignSnapPDFPath;
    }

    private void generatePDFToDisk(String requestSN, String noSignSnapPDFPath, String pdfData) throws Exception {
        OutputStream out = null;
        try {
            // 4.生成PDF合同文件
            out = new FileOutputStream(noSignSnapPDFPath);
            PdfGenerator.generate(pdfData, out);
        } catch (Exception e) {
            log.error("－消息－:requestSn={},生成pdf失败", requestSN, e);
            throw new RuntimeException(e);
        } finally {
            if (out != null) {
                out.close();
            }
        }
    }

    /**
     * 获取拼装好参数的模板字符串
     *
     * @param templateParams
     * @param templateNo
     * @param requestSN
     * @return
     * @throws IOException
     * @throws InterruptedException
     */
    private String getPDFStr(Map<String, Object> templateParams, String templateNo, String requestSN) throws Exception {

        // 获取模板快照名称
        String templatTempName = getSnapTemplate(templateNo, requestSN);

        // Freemarker获取模板文件
        Configuration config = FreemarkerConfiguration.getConfiguation();
        freemarker.template.Template tp = config.getTemplate(templatTempName, "UTF-8");

        BufferedWriter writer = null;
        // 拼装参数到模板
        try {
            StringWriter stringWriter = new StringWriter();
            writer = new BufferedWriter(stringWriter);
            tp.process(templateParams, writer);
            String templataData = stringWriter.toString();
            writer.flush();
            return templataData;
        } catch (Exception e) {
            log.error("－消息－:requestSn={},拼装pdf模板数据失败", requestSN);
            throw new RuntimeException(e);
        } finally {
            if (writer != null) {
                writer.close();
            }
        }
    }


    /**
     * 获取模板快照
     *
     * @param templateNo
     * @param requestSN
     * @return
     * @throws IOException
     * @throws InterruptedException
     */
    private String getSnapTemplate(String templateNo, String requestSN) throws Exception {
        Template template = templateService.selectByTemplateNo(templateNo);
        if (template == null) {
            log.error("模板不存在,模板编号为:{}", templateNo);
            throw new Exception("模板不存在");
        }

        String snapTemplateName = CoreConstant.TEMPLATE_FILENAME_PREFIX + templateNo + "_" + template.getVersion() + CoreConstant.FTL_EXT;
        String freemarkerBasePath = FreemarkerConfiguration.getFreemarkerFileDirPath();
        String snapTemplatePath = freemarkerBasePath + snapTemplateName;
        File file = new File(snapTemplatePath);
        if (file.exists()) {
            return snapTemplateName;
        }

        generateSnapTemplate(templateNo, template.getRefId(), freemarkerBasePath, snapTemplatePath, file, requestSN);

        return snapTemplateName;
    }

    /**
     * 生成当前节点应用级模板快照，每个模板保证只有一份快照
     *
     * @param templateNo
     * @param templateExtId
     * @param freemarkerBasePath
     * @param snapTemplatePath
     * @param requestSN
     * @param file
     * @throws InterruptedException
     * @throws IOException
     */
    private void generateSnapTemplate(String templateNo, Integer templateExtId, String freemarkerBasePath,
                                      String snapTemplatePath, File file, String requestSN) throws InterruptedException, IOException {
        if (templatesSemaphore.get(templateNo) == null) {
            synchronized (this) {
                if (templatesSemaphore.get(templateNo) == null) {
                    templatesSemaphore.put(templateNo, new Semaphore(1));
                }
            }
        }
        Semaphore semaphore = templatesSemaphore.get(templateNo);
        FileOutputStream fos = null;
        try {
            semaphore.acquire();
            if (!file.exists()) {
                File basePath = new File(freemarkerBasePath);
                if (!basePath.exists()) {
                    basePath.mkdirs();
                }
                TemplateExt templateExt = templateExtService.selectByPrimaryKey(templateExtId);
                fos = new FileOutputStream(snapTemplatePath);
                fos.write(templateExt.getTemplateData());
            }
        } catch (Exception e) {
            log.error("－消息－:requestSn={},生成模板快照异常", requestSN);
            throw new RuntimeException(e);
        } finally {
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    // 不做处理
                }
            }
            semaphore.release();
        }
    }

    /**
     * 获取未签章pdf文件路径
     *
     * @param requestSN
     * @return
     */
    private String getSnapPDFPath(String requestSN) {
        String pdfName = CoreConstant.SNAP_PDF_PREFIX + requestSN + CoreConstant.PDF_EXT;
        String date = DateUtil.getStandardDateStr();
        File snapPDFDictory = new File(snapNoSignPDFPath + File.separator + date);
        if (!snapPDFDictory.exists()) {
            snapPDFDictory.mkdirs();
        }
        return snapNoSignPDFPath + File.separator + date + File.separator + pdfName;
    }

    /**
     * 生成模板参数
     *
     * @throws Exception
     */
    private Map<String, Object> generateTemplateParams(Map<String, List<Map<String, String>>> listData,
                                                       Map<String, String> otherData) throws Exception {

        Map<String, Object> templateData = new HashMap<String, Object>();

        if (otherData != null && !otherData.isEmpty()) {
            templateData.putAll(otherData);
        }
        if (listData != null && !listData.isEmpty()) {
            templateData.putAll(listData);
        }

        // 判断合同版本号是否存在
        Object userversion = templateData.get("_userversion_");
        if (userversion == null || StringUtils.isBlank(userversion.toString())) {
            templateData.put("_userversion_", "v1.0");
        }
        return templateData;
    }
}
